package com.qd.panda.service.park;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.math.Money;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cdqidi.constant.ConstantDto;
import com.cdqidi.dto.CacheKey;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.RedisTemplateUtil;
import com.cdqidi.util.SpringContextHolder;
import com.qd.common.panda.constant.PandaConstant;
import com.qd.common.panda.domain.entity.card.UserCardDTO;
import com.qd.common.panda.domain.entity.card.UserCardQueryDTO;
import com.qd.common.panda.domain.entity.carpark.GasCalculatePriceDTO;
import com.qd.common.panda.domain.entity.carpark.calculate.CalculatePriceDTO;
import com.qd.common.panda.domain.entity.order.GasCalculatePriceOrderDTO;
import com.qd.common.panda.domain.entity.stroke.PayParamDTO;
import com.qd.common.panda.domain.enums.GasStationCompanyNameEnum;
import com.qd.common.panda.domain.enums.GasStationTypeEnum;
import com.qd.common.sys.domain.entity.log.SendLogDTO;
import com.qd.common.sys.event.SendLogEvent;
import com.qd.common.sys.util.SecurityUtils;
import com.qd.panda.gas.station.base.GasHttpService;
import com.qd.panda.gas.station.base.GasHttpServiceInitApplicationContextAware;
import com.qd.panda.gas.station.req.ComputeReqDTO;
import com.qd.panda.gas.station.resp.PayRespDTO;
import com.qd.panda.model.CarPark;
import com.qd.panda.model.CarParkGasStation;
import com.qd.panda.model.UserIntegral;
import com.qd.panda.service.card.UserCardHolderService;
import com.qd.panda.service.card.UserCardService;
import com.qd.panda.service.integral.UserIntegralService;
import com.qd.panda.service.stroke.BigDecimalUtil;
import com.qd.panda.service.stroke.DisplayUtil;
import com.qd.panda.service.stroke.UserStrokeHolderService;
import com.qd.panda.service.stroke.order.GasOrderService;
import com.qd.panda.service.stroke.order.UserStrokeOrderService;
import com.qd.system.service.system.SystemSetService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 加油站相关操作
 *
 * @author sjk
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class GasStationHolderService {
    /**
     * 积分抵扣规则，1000积分抵扣一块
     */
    public static final Double DEDUCTION = 0.001;
    /**
     * 积分抵扣规则为1000抵扣一块，只能是1000的倍数
     */
    private static final Integer POINT = 1000;
    private static final String PRICE_EXISTS = ".";
    private static final Integer POINT_LENGTH = 2;
    private final CarParkService carParkService;
    private final CarParkGasStationService carParkGasStationService;
    private final CarParkGasStationNozzleService gasStationNozzleService;
    private final UserIntegralService userIntegralService;
    private final UserStrokeHolderService userStrokeHolderService;
    private final UserStrokeOrderService userStrokeOrderService;
    private final UserCardHolderService userCardHolderService;
    private final RedisTemplateUtil<CalculatePriceDTO> redisTemplateUtil;
    private final SystemSetService systemSetService;
    private final GasOrderService gasOrderService;
    private final UserCardService userCardService;

    /**
     * 计算折扣价格
     *
     * @param calculatePriceDTO 参数
     * @return 折后价
     */
    @Transactional(readOnly = true)
    public CalculatePriceDTO calculatePrice(GasCalculatePriceDTO calculatePriceDTO) {
        //现在不判断油号
        //检查油号是否正确
        //GasEnum.checkOctaneNumber(calculatePriceDTO.getOctaneNumber());
        BigDecimal originalPrice = getBigDecimalPrice(calculatePriceDTO);
        CarPark carPark = Optional.ofNullable(carParkService.getById(calculatePriceDTO.getParkId())).orElseThrow(() -> new ApiException("加油站不存在"));
        if (!carPark.getParkType().equals(PandaConstant.PARK_TYPE_GAS_STATION)) {
            throw new ApiException("当前站点不是加油站");
        }
        CalculatePriceDTO calculatePrice = calculatePrice(calculatePriceDTO, originalPrice);
        calculatePrice.setId(IdWorker.getIdStr());
        if (StringUtils.hasLength(calculatePriceDTO.getCardId())) {
            BigDecimal amount = userCardHolderService.buildCard(calculatePriceDTO.getCardId(), calculatePrice.getPrice(), calculatePriceDTO.getUseBrand());
            //按优惠卷计算，不管之前的折扣价格
            calculatePrice.setCalculatePrice(amount);
            calculatePrice.setCouponAmount(new Money(calculatePrice.getPrice()).subtract(new Money(calculatePrice.getCalculatePrice())).getAmount());
            calculatePrice.setDiscountAmount(calculatePrice.getCouponAmount());
        } else {
            if (StringUtils.hasLength(calculatePriceDTO.getCardType()) && StringUtils.hasLength(calculatePriceDTO.getUseBrand())) {
                UserCardQueryDTO queryDTO = new UserCardQueryDTO();
                queryDTO.setUserId(SecurityUtils.getLoginUser().getUserId());
                queryDTO.setState(PandaConstant.USE_STATE_1);
                queryDTO.setCardType(calculatePriceDTO.getCardType());
                queryDTO.setUseBrand(calculatePriceDTO.getUseBrand());
                queryDTO.setPrice(calculatePriceDTO.getPrice());
                UserCardDTO userCardDTO = userCardHolderService.getCardMax(queryDTO);
                queryDTO.freeData();
                if (null != userCardDTO) {
                    BigDecimal cardCalculatePrice = BigDecimalUtil.buildDefaultScale(userCardDTO.getPreferentialAmount(), 2);
                    BigDecimal cardDiscountAmount = new Money(calculatePrice.getPrice()).subtract(new Money(cardCalculatePrice)).getAmount();
                    Map<String, Object> map = buildUseCard(userCardDTO.getCardId(), cardCalculatePrice, cardCalculatePrice.toPlainString(), cardDiscountAmount, cardDiscountAmount.toPlainString());
                    calculatePrice.setUserCard(map);
                    userCardDTO.freeData();
                }
            }
        }
        if (StringUtils.hasLength(calculatePriceDTO.getIntegral())) {
            validatorIntegral(calculatePriceDTO.getIntegral());
            //计算积分抵扣的金额是多少
            Money money = new Money(calculatePriceDTO.getIntegral()).multiplyBy(GasStationHolderService.DEDUCTION);
            calculatePrice.setCalculatePrice(new Money(calculatePrice.getCalculatePrice()).subtract(money).getAmount());
            calculatePrice.setIntegralAmount(money.getAmount());
        }
        carParkGasStationService.buildDisplay(calculatePrice);
        if (StringUtils.hasLength(calculatePriceDTO.getCardId())) {
            //三虎要求的，有了卡号以后价格是按照卡的价格算的
            Map<String, Object> map = buildUseCard(calculatePriceDTO.getCardId(), calculatePrice.getCalculatePrice(), calculatePrice.getCalculatePriceDisplay(), calculatePrice.getDiscountAmount(), calculatePrice.getDiscountAmountDisplay());
            calculatePrice.setUserCard(map);
        }
        log.info("调用接口计算价格，油站所属公司: {},原价: {},优惠价: {},立减优惠金额: {},优惠券金额: {},积分优惠金额: {},服务费: {},", carPark.getName(), originalPrice, calculatePrice.getCalculatePriceDisplay(), calculatePrice.getDiscountAmount(), calculatePrice.getCouponAmountDisplay(), calculatePrice.getIntegralAmountDisplay(), calculatePrice.getServiceFeeDisplay());
        //放入缓存，等到创建订单的时候直接从缓存中获得
        redisTemplateUtil.set(new CacheKey().setKey(calculatePrice.getId()).setTimeout(ConstantDto.REDIS_TIME_OUT_MIN * 2), calculatePrice);
        return calculatePrice;
    }

    private Map<String, Object> buildUseCard(String cardId, BigDecimal calculatePrice, String calculatePriceDisplay, BigDecimal discountAmount, String discountAmountDisplay) {
        UserCardDTO userCard = Optional.ofNullable(userCardService.getByIdDto(cardId)).orElseThrow(() -> new ApiException("cardId值错误"));
        Map<String, Object> map = new HashMap<>(5);
        map.put("cardId", cardId);
        map.put("title", userCard.getCardInfoDTO().getTitle());
        map.put("cardCalculatePrice", calculatePrice);
        map.put("cardCalculatePriceDisplay", calculatePriceDisplay);
        map.put("cardDiscountAmount", discountAmount);
        map.put("cardDiscountAmountDisplay", discountAmountDisplay);
        return map;
    }


    @Transactional(rollbackFor = Exception.class)
    public PayParamDTO createOrder(GasCalculatePriceOrderDTO dto) {
        if (null == dto.getOctaneNumber()) {
            final Integer octaneNumber = Optional.ofNullable(gasStationNozzleService.getOctaneNumberByPIdAndNozzleNumber(dto.getParkId(), dto.getNozzleNumber()))
                    .orElse(92);
            dto.setOctaneNumber(octaneNumber);
        }
        final LocalDateTime now = LocalDateTime.now();
        final CarParkGasStation carParkGasStation = Optional.ofNullable(carParkGasStationService.getById(dto.getParkId())).orElseThrow(() -> new ApiException("加油站不存在"));
        if (!StringUtils.hasLength(carParkGasStation.getStationId())) {
            throw new ApiException("加油站ID为空");
        }
        String gasStationCompanyName = carParkGasStation.getGasStationCompanyName();
        String userId = SecurityUtils.getLoginUser().getUserId();
        if (gasStationCompanyName.equals(GasStationCompanyNameEnum.E_JIA_YOU.getCode())) {
            if (SpringContextHolder.getActiveProfile().equals(SpringContextHolder.PROFILE_OFFICIAL)) {
                //民营加油站每个用户每个站每天两单
                if (userStrokeOrderService.countGasOrder(userId, dto.getParkId(), GasStationCompanyNameEnum.E_JIA_YOU.getCode()) > 1) {
                    throw new ApiException("同一用户每天最多享两次优惠加油，明天见哟！");
                }
            }
        }
        if (gasStationCompanyName.equals(GasStationCompanyNameEnum.EXTEND_SHELL.getCode()) || gasStationCompanyName.equals(GasStationCompanyNameEnum.EXTEND_SHELL_HIGH.getCode())) {
            //壳牌每个用户每天两单
            if (SpringContextHolder.getActiveProfile().equals(SpringContextHolder.PROFILE_OFFICIAL)) {
                if (userStrokeOrderService.countGasOrder(userId, dto.getParkId(), GasStationCompanyNameEnum.EXTEND_SHELL.getCode()) > 1) {
                    throw new ApiException("同一用户每天最多享两次优惠加油，明天见哟！");
                }
                if (userStrokeOrderService.countGasOrderByMonth(userId, dto.getParkId(), GasStationCompanyNameEnum.EXTEND_SHELL.getCode()) > 9) {
                    throw new ApiException("同一用户每月最多享十次优惠加油，下月见哟！");
                }
            }
        }
        //是否需要自己计算价格
        boolean b = true;
        //从缓存中获得计算的价格，如果没有，在进行计算
        CalculatePriceDTO stationCalculatePrice = redisTemplateUtil.get(dto.getId(), CalculatePriceDTO.class);
        if (null != stationCalculatePrice) {
            if (stationCalculatePrice.getPrice().compareTo(new BigDecimal(dto.getPrice())) == 0) {
                //缓存价格和传入价格相等，表示不需要重新计算
                b = false;
            }
        }
        if (StringUtils.hasLength(dto.getCardId())) {
            b = true;
        }
        if (b) {
            GasCalculatePriceDTO query = new GasCalculatePriceDTO();
            query.setParkId(dto.getParkId())
                    .setCardId(dto.getCardId())
                    .setOctaneNumber(dto.getOctaneNumber())
                    .setNozzleNumber(dto.getNozzleNumber())
                    .setPrice(dto.getPrice());
            stationCalculatePrice = calculatePrice(query);
            query.freeData();
        }
        String orderId = IdWorker.getIdStr();
        if (StringUtils.hasLength(dto.getCardId())) {
            userCardHolderService.validatorAndGetCardInfo(dto.getCardId(), stationCalculatePrice.getPrice(), PandaConstant.CARD_TYPE_GAS, null);
            gasOrderService.buildCardInfo(orderId, dto.getCardId(), stationCalculatePrice);
        }
        if (StringUtils.hasLength(dto.getIntegral())) {
            validatorIntegral(dto.getIntegral());
            gasOrderService.buildUserIntegral(orderId, dto.getIntegral(), stationCalculatePrice);
        }
        final PayRespDTO payRespDTO = gasOrderService.createGasOrder(orderId, carParkGasStation, dto, stationCalculatePrice.getPrice(), stationCalculatePrice.getRefuelingAmount());
        final BigDecimal oilMass = Optional.ofNullable(payRespDTO).map(PayRespDTO::getOilMass).orElse(null);
        final String thirdPartyOrderId = Optional.ofNullable(payRespDTO).map(PayRespDTO::getOrderId).orElse(null);
        final PayParamDTO orderPay = userStrokeHolderService.createOrderPay(dto.getParkId(), orderId, stationCalculatePrice.getCalculatePrice(), DisplayUtil.ORDER_TYPE_4);
        gasOrderService.saveOrder(orderId, dto.getParkId(), now, stationCalculatePrice);
        gasOrderService.saveOrderGas(orderId, dto, oilMass);

        gasOrderService.savePreferentialLog(orderId, thirdPartyOrderId, now, stationCalculatePrice);
        gasOrderService.buildToRedis(orderId, thirdPartyOrderId, carParkGasStation.getGasStationCompanyName(), stationCalculatePrice, dto);
        carParkGasStation.freeData();
        stationCalculatePrice.freeData();
        return orderPay;
    }

    /**
     * 计算价格
     *
     * @param calculatePriceDTO 前端参数
     * @param originalPrice     原价
     * @return 价格
     */
    private CalculatePriceDTO calculatePrice(GasCalculatePriceDTO calculatePriceDTO, BigDecimal originalPrice) {
        CarParkGasStation carParkGasStation = Optional.ofNullable(carParkGasStationService.getById(calculatePriceDTO.getParkId())).orElseThrow(() -> new ApiException("加油站扩展信息不存在"));
        if (carParkGasStation.getGasStationCompanyName().equals(GasStationCompanyNameEnum.EXTEND_SHELL_OTHER.getCode())) {
            if (carParkGasStation.getStationType().equals(GasStationTypeEnum.PETRO_CHINA.getCode())) {
                throw new ApiException("中石油正在调试中");
            }
        }
        GasHttpService gasHttpService = GasHttpServiceInitApplicationContextAware.getGasHttpService(carParkGasStation.getGasStationCompanyName());
        long start = System.currentTimeMillis();
        SendLogDTO sendLogDTO = new SendLogDTO();
        sendLogDTO.setAddTime(LocalDateTime.now());
        sendLogDTO.setLogType(PandaConstant.LOG_TYPE_GAS_CALCULATE_PRICE);
        sendLogDTO.setSendState(ConstantDto.SF1);
        ComputeReqDTO payReqDTO = new ComputeReqDTO()
                .setParkId(carParkGasStation.getPId())
                .setStationId(carParkGasStation.getStationId())
                .setTotalAmount(originalPrice)
                .setSendLogDTO(new SendLogDTO())
                .setMobile(SecurityUtils.getLoginUser().getUserId())
                .setNozzleNumber(calculatePriceDTO.getNozzleNumber())
                .setSendLogDTO(sendLogDTO);
        try {
            try {
                CalculatePriceDTO calculatePrice = gasHttpService.compute(payReqDTO);
                payReqDTO.freeData();
                return calculatePrice;
            } finally {
                if (null != payReqDTO.getSendLogDTO()) {
                    long end = System.currentTimeMillis();
                    sendLogDTO.setTimeConsumer(end - start);
                    SpringContextHolder.publishEvent(new SendLogEvent(sendLogDTO));
                }
            }
        } catch (Exception e) {
            final String message = ExceptionUtil.getMessage(e);
            if (null != payReqDTO.getSendLogDTO()) {
                sendLogDTO.setErrMsg(message);
                sendLogDTO.setSendState(ConstantDto.SF0);
            }
            throw e;
        }
    }

    private void validatorIntegral(String integral) {
        if (Optional.ofNullable(systemSetService.getById(PandaConstant.POINT_DEDUCTION)).map(sys -> sys.getValue().replace("\"", "")).orElse(ConstantDto.SF0).equals(ConstantDto.SF0)) {
            throw new ApiException("未开启积分抵扣");
        }
        if (integral.contains(".")) {
            throw new ApiException("积分只能是整数");
        }
        BigDecimal integralBigDecimal = new BigDecimal(integral);
        if (integralBigDecimal.compareTo(BigDecimal.ZERO) != BigDecimal.ONE.signum()) {
            throw new ApiException("积分只能是正整数");
        }
        BigDecimal[] results = integralBigDecimal.divideAndRemainder(BigDecimal.valueOf(POINT));
        if (results[1].intValue() > 0) {
            throw new ApiException("积分兑换只能是" + POINT + "的倍数");
        }
        UserIntegral userIntegral = Optional.ofNullable(userIntegralService.getById(SecurityUtils.getLoginUser().getUserId())).orElseThrow(() -> new ApiException("没有可用的积分"));
        if (userIntegral.getIntegral().compareTo(integralBigDecimal) < 0) {
            throw new ApiException("积分不够");
        }
    }

    private BigDecimal getBigDecimalPrice(GasCalculatePriceDTO calculatePriceDTO) {
        String price = calculatePriceDTO.getPrice();
        if (price.contains(PRICE_EXISTS)) {
            int priceLength = price.length();
            int start = price.indexOf(PRICE_EXISTS) + 1;
            if (start > priceLength) {
                throw new ApiException("金额输入错误");
            }
            if (start == priceLength) {
                //说明只有".",没有输入小数点后的数字,自动加0
                price += "00";
            }
            final int len = price.substring(start).length();
            if (len > POINT_LENGTH) {
                throw new ApiException("金额只能支持小数点后两位");
            }
        }
        BigDecimal originalPrice;
        try {
            originalPrice = new BigDecimal(calculatePriceDTO.getPrice());
        } catch (Exception e) {
            throw new ApiException("金额输入错误");
        }
        if (originalPrice.doubleValue() > GasCalculatePriceDTO.MAX_AMOUNT) {
            throw new ApiException("单次加油金额不能大于5000元");
        }
        if (originalPrice.doubleValue() < GasCalculatePriceDTO.MIN_AMOUNT) {
            throw new ApiException("单次加油金额不能小于1元");
        }
        return originalPrice;
    }
}